﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IsmsExplorer.Model.Documents;
using IsmsExplorer.Infrastructure.Helper;
using IsmsExplorer.Model.Projects;
using IsmsExplorer.Infrastructure.RepositoryFramework;
using System.Diagnostics;
using System.Data;

namespace IsmsExplorer.Infrastructure.Repositories.Documents {
    internal class IsmsSpecificDocumentationRepository : SqlServerRepositoryBase<IsmsSpecificDocumentation>, IIsmsSpecificDocumentationRepository {
        #region Data
        #endregion

        #region Constructors
        public IsmsSpecificDocumentationRepository(IUnitOfWork unitOfWork) : base(unitOfWork) {
        }

        public IsmsSpecificDocumentationRepository() : this(null) {
        }
        #endregion

        // Need to override some of the IRepository<T> methods as this class is special and 
        // does NOT make sense to use some of the API.
        #region IRepository<T> implementation overrides

        public override IsmsSpecificDocumentation FindBy(object id) {
            throw new NotImplementedException("");
        }

        public override IList<IsmsSpecificDocumentation> FindAll() {
            throw new NotImplementedException("");
        }

        public override IsmsSpecificDocumentation this[object id] {
            get { throw new NotImplementedException(""); }
            set { throw new NotImplementedException(""); }
        }

        #endregion

        #region IIsmsSpecificDocumentationRepository members

        public IsmsSpecificDocumentation FindByDocumentType(int projectID, int documentTypeID) {
            string sql = string.Concat(GetBaseQuery(), BuildBaseWhereClause(projectID, documentTypeID));
            return this.BuildEntityFromSql(sql);
        }
       
        public bool ExistFor(int projectID, int documentTypeID) {
            string sql = string.Format(@"SELECT TOP 1 {0} FROM [IsmsSpecificDocuments] WHERE {1}={2} AND {3}={4}",
                IsmsSpecificDocumentationFactory.FieldNames.DocumentID,
                IsmsSpecificDocumentationFactory.FieldNames.ProjectID,
                DataHelper.GetSqlValue(projectID),
                IsmsSpecificDocumentationFactory.FieldNames.DocumentTypeID,
                DataHelper.GetSqlValue(documentTypeID));
            using (IDataReader reader = this.ExecuteReader(sql)) {
                return reader.Read();
            }
        }

        public IsmsSpecificDocumentation this[int projectID, int documentTypeID] {
            get {
                return FindByDocumentType(projectID, documentTypeID);
            }
            set {
                // ExistFor() is faster than FindByControl(,) as it does not have to load all subobjects!
                if (!ExistFor(projectID, documentTypeID)) {
                    Add(value);

                } else {
                    if (_unitOfWork != null) {
                        _unitOfWork.RegisterUpdated(value, this);
                    }
                }
            }
        }        

        #endregion

        #region BuildChildDataCallbacks
        protected override void InitialiseChildCallbacks() {
            // Need to insert callbacks to build child objects: Control.
            // NOTE: Currently commented out as this is explicitely called within FindByControl().
            //this.ChildCallbacks.Add(IsmsSpecificDocumentationFactory.FieldNames.ControlID, this.AppendIso27001Control);           
        }
        #endregion

        #region Child object callbacks        
        #endregion

        #region BuildEntityFromSql override (to get the IsmsDocumentLinks associated with IsmsSpecificDocumentation).

        protected override IsmsSpecificDocumentation BuildEntityFromSql(string sql) {
            IsmsSpecificDocumentation obj = base.BuildEntityFromSql(sql);
            // Get the associated documents.
            IIsmsDocumentLinkRepository r = RepositoryFactory.GetRepository<IIsmsDocumentLinkRepository, IsmsDocumentLink>();
            IList<IsmsDocumentLink> docs = r.GetDocumentsByTypeID(obj.ProjectID, obj.DocumentTypeID);
            foreach (var doc in docs) {
                obj.AddDocument(doc);
            }
            return obj;
        }
        #endregion

        #region GetBaseQuery
        protected override string GetBaseQuery() {
            return string.Format(@"SELECT {0},{1},{2},{3} FROM [IsmsSpecificDocuments]",
                IsmsSpecificDocumentationFactory.FieldNames.DocumentID,
                IsmsSpecificDocumentationFactory.FieldNames.Description,
                IsmsSpecificDocumentationFactory.FieldNames.DocumentTypeID,
                IsmsSpecificDocumentationFactory.FieldNames.ProjectID
                );
        }
        #endregion

        #region GetBaseWhereClause
        protected override string GetBaseWhereClause() {
            StringBuilder sb = new StringBuilder(" WHERE ");
            sb.Append(IsmsSpecificDocumentationFactory.FieldNames.ProjectID).Append("={0} AND ")
              .Append(IsmsSpecificDocumentationFactory.FieldNames.DocumentTypeID).Append("={1}");
            return sb.ToString();
        }
        #endregion

        protected override string BuildBaseWhereClause(object id) {
            throw new NotImplementedException("");
        }
        protected virtual string BuildBaseWhereClause(int projectID, int documentTypeID) {
            return string.Format(GetBaseWhereClause(), projectID, documentTypeID);
        }

        #region UnitOfWork implementation

        protected override void PersistDeletedItem(IsmsSpecificDocumentation item) {
            string sql = string.Format(@"DELETE FROM [IsmsSpecificDocuments] {0}", this.BuildBaseWhereClause(item.ProjectID, item.DocumentTypeID));
            this.Database.ExecuteNonQuery(this.Database.GetSqlStringCommand(sql));
        }

        protected override void PersistNewItem(IsmsSpecificDocumentation item) {
            // Need to insert an association between the type and each document(s) in this object.
            StringBuilder sb = new StringBuilder(256);
            sb.AppendFormat(@"INSERT INTO [IsmsSpecificDocuments]({0},{1},{2},{3})",
                IsmsSpecificDocumentationFactory.FieldNames.ProjectID,
                IsmsSpecificDocumentationFactory.FieldNames.DocumentTypeID,
                IsmsSpecificDocumentationFactory.FieldNames.Description,
                IsmsSpecificDocumentationFactory.FieldNames.DocumentID)
            .AppendFormat(@" VALUES ({0},{1},{2}",
                DataHelper.GetSqlValue(item.ProjectID),
                DataHelper.GetSqlValue(item.DocumentTypeID),
                DataHelper.GetSqlValue(item.Description))          
            .Append(@",{0});");
            foreach (IsmsDocumentLink doc in item.Documents) {
                int rowsAffected = this.Database.ExecuteNonQuery(this.Database.GetSqlStringCommand(string.Format(sb.ToString(), DataHelper.GetSqlValue(doc.DocumentID))));
                Debug.Assert(rowsAffected > 0);
            }
        }

        protected override void PersistUpdatedItem(IsmsSpecificDocumentation item) {
            // Updated means there's different documents associated with the control.
            // We first delete all previous associations (for (control, project)) and then reinsert this (changed) item.
            PersistDeletedItem(item);
            PersistNewItem(item);
        }
        #endregion

        #region Private helper/callback methods
        #endregion

    } // end internal class IsmsSpecificDocumentationRepository : SqlServerRepositoryBase<IsmsSpecificDocumentation>, IIsmsSpecificDocumentationRepository

}
